package com.quotes.indicators;

import java.util.*;
import com.quotes.datamodell.*;

public class AdxIndicator
{
	private TimeSeries adx = null;
	private TimeSeries diMinus = null;
	private TimeSeries diPlus = null;
	private TimeSeries atr = null;
	
	public AdxIndicator(Quotes quotes, int ticks) {
		List<Date> dates = quotes.getQuote(QuotesType.CLOSE).DatesAsList();
		List<Double> highPrices = quotes.getQuote(QuotesType.HIGH).ValuesAsList();
		List<Double> lowPrices = quotes.getQuote(QuotesType.LOW).ValuesAsList();
		List<Double> closePrices = quotes.getQuote(QuotesType.CLOSE).ValuesAsList();
		
		List<Double> trueRange = calculateTrueRange(highPrices, lowPrices, closePrices);
		List<Double> atrValue = wildersSmoothAverage(trueRange, ticks, 1);
		atr = new TimeSeries(dates, atrValue);
		
		List<Double> diMinusValues = calculateDiMinus(highPrices, lowPrices, closePrices, ticks);
		diMinus = new TimeSeries(dates, diMinusValues);
		
		List<Double> diPlusValues = calculateDiPlus(highPrices, lowPrices, closePrices, ticks);
		diPlus = new TimeSeries(dates, diPlusValues);
		
		List<Double> adxValues = calculateADX(highPrices, lowPrices, closePrices, ticks);
    	adx = new TimeSeries(dates, adxValues);
	}
	
	public TimeSeries getAdx() {
		return adx;
	}
	
	public TimeSeries getDiMinus() {
		return diMinus;
	}
	
	public TimeSeries getDiPlus() {
		return diPlus;
	}
	
	public TimeSeries getAtr() {
		return atr;
	}
	
	public boolean isAdxHigherOrEqual(Date date, double boundary) {
		double value = adx.getOr0(date);
		return (value >= boundary);
	}
	
	public boolean isAdxLower(Date date, double boundary) {
		double value = adx.getOr0(date);
		return (value < boundary);
	}
	
	public boolean isDiPlusAboveDiMinus(Date date) {
		return diPlus.getOr0(date) >= diMinus.getOr0(date);
	}
	
	public static TimeSeries calculateAverageTrueRange(Quotes quotes, int ticks) {
		List<Date> dates = quotes.getQuote(QuotesType.CLOSE).DatesAsList();
		List<Double> highPrices = quotes.getQuote(QuotesType.HIGH).ValuesAsList();
		List<Double> lowPrices = quotes.getQuote(QuotesType.LOW).ValuesAsList();
		List<Double> closePrices = quotes.getQuote(QuotesType.CLOSE).ValuesAsList();
		
		List<Double> trueRange = calculateTrueRange(highPrices, lowPrices, closePrices);
		List<Double> atrValue = wildersSmoothAverage(trueRange, ticks, 1);
		return new TimeSeries(dates, atrValue);
	}
	
	public static List<Double> calculateTrueRange(List<Double> highPrices, List<Double> lowPrices, List<Double> closePrices) {
		List<Double> result = createListOfZeroDoubles(highPrices.size());
		
		for (int i=1; i<highPrices.size(); i++) {
			double a = highPrices.get(i) - lowPrices.get(i);
			double b = Math.abs(closePrices.get(i-1) - highPrices.get(i));
			double c = Math.abs(closePrices.get(i-1) - lowPrices.get(i));
			result.set(i, Math.max(a, Math.max(b, c)));
		}
		
		return result;
	}
	
	private static List<Double> createListOfZeroDoubles(int count) {
		List<Double> result = new ArrayList<Double>();
		for (int i=0; i<count; i++) {
			result.add(0d);
		}
		
		return result;
	}
	
	public static List<Double> wildersSmoothExponential(List<Double> data, int ticks, int deltaStart) {
		List<Double> result = createListOfZeroDoubles(data.size());
		if (data.size() < ticks + deltaStart)
			return result;
		
		double sum = 0;
		for (int i=deltaStart; i<ticks+deltaStart; i++) { 
			sum += data.get(i);
		}
		
		result.set((ticks-1)+deltaStart, sum);
		
		for (int i=ticks+deltaStart; i<data.size(); i++) {
			result.set(i, result.get(i-1) - (result.get(i-1) / ticks) + data.get(i));
		}

		return result;
	}
	
	public static List<Double> wildersSmoothAverage(List<Double> data, int ticks, int deltaStart) {
		List<Double> result = createListOfZeroDoubles(data.size());
		if (data.size() < ticks + deltaStart)
			return result;
		
		double sum = 0;
		for (int i=deltaStart; i<ticks+deltaStart; i++) { 
			sum += data.get(i);
		}
		
		result.set((ticks-1)+deltaStart, sum / ticks);
		
		for (int i=ticks+deltaStart; i<data.size(); i++) {
			result.set(i, (result.get(i-1)*(ticks-1) + data.get(i)) / ticks);
		}

		return result;
	}
	
	public static List<Double> calculateDMPlus(List<Double> highPrices, List<Double> lowPrices) {
		List<Double> result = createListOfZeroDoubles(highPrices.size());
		
		for (int i=1; i<highPrices.size(); i++) {
			if (highPrices.get(i) - highPrices.get(i-1) > lowPrices.get(i-1) - lowPrices.get(i)) {
				result.set(i, Math.max(0, highPrices.get(i) - highPrices.get(i-1)));
			}
		}
		
		return result;
	}
	
	public static List<Double> calculateDMMinus(List<Double> highPrices, List<Double> lowPrices) {
		List<Double> result = createListOfZeroDoubles(highPrices.size());
		
		for (int i=1; i<highPrices.size(); i++) {
			if (lowPrices.get(i-1) - lowPrices.get(i) > highPrices.get(i) - highPrices.get(i-1)) {
				result.set(i, Math.max(0, lowPrices.get(i-1) - lowPrices.get(i)));
			}
		}
		
		return result;
	}
	
	public static List<Double> calculateDiPlus(List<Double> highPrices, List<Double> lowPrices, List<Double> closePrices, int ticks) {
		List<Double> result = createListOfZeroDoubles(highPrices.size());

		List<Double> dmPlus = calculateDMPlus(highPrices, lowPrices);
		List<Double> trueRange = calculateTrueRange(highPrices, lowPrices, closePrices);
		List<Double> trueRangeSmoothed = wildersSmoothExponential(trueRange, ticks, 1);
		List<Double> dmPlusSmoothed = wildersSmoothExponential(dmPlus, ticks, 1);
		
		for (int i=0; i<highPrices.size(); i++) {
			double diPlus = dmPlusSmoothed.get(i) / trueRangeSmoothed.get(i);
			result.set(i, diPlus);
		}
		
		return result;
	}
	
	public static List<Double> calculateDiMinus(List<Double> highPrices, List<Double> lowPrices, List<Double> closePrices, int ticks) {
		List<Double> result = createListOfZeroDoubles(highPrices.size());

		List<Double> dmMinus = calculateDMMinus(highPrices, lowPrices);
		List<Double> trueRange = calculateTrueRange(highPrices, lowPrices, closePrices);
		List<Double> trueRangeSmoothed = wildersSmoothExponential(trueRange, ticks, 1);
		List<Double> dmMinusSmoothed = wildersSmoothExponential(dmMinus, ticks, 1);
		
		for (int i=0; i<highPrices.size(); i++) {
			double diMinus = dmMinusSmoothed.get(i) / trueRangeSmoothed.get(i);
			result.set(i, diMinus);
		}
		
		return result;
	}
	
	public static List<Double> calculateDX(List<Double> highPrices, List<Double> lowPrices, List<Double> closePrices, int ticks) {
		List<Double> diPlus = calculateDiPlus(highPrices, lowPrices, closePrices, ticks);
		List<Double> diMinus = calculateDiMinus(highPrices, lowPrices, closePrices, ticks);
		List<Double> dx = createListOfZeroDoubles(highPrices.size());

		for (int i=0; i<highPrices.size(); i++) {
			double value = Math.abs((diPlus.get(i) - diMinus.get(i)) / (diPlus.get(i) + diMinus.get(i))) * 100;
			
			if (Double.isNaN(value))
				value = 0;
			
			dx.set(i, value);
		}

		return dx;
	}
	
	public static List<Double> calculateADX(List<Double> highPrices, List<Double> lowPrices, List<Double> closePrices, int ticks) {
		List<Double> dx = calculateDX(highPrices, lowPrices, closePrices, ticks);
		return wildersSmoothAverage(dx, ticks, ticks);
	}
}
